#include<cstdio>
#include<stdlib.h>
#include<string>
#include<string.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>
using namespace std;

const int basesize = 1024;
const int argvnum = 64;
const int envnum = 64;
//存储命令行参数的两个全局变量
char* g_argv[argvnum];
int g_argc;
//存shell自己的环境变量
char* g_env[envnum];

//如果这两个设置为局部变量，则会写入一个空白
//局部变量销毁，环境变量表中存的是这两个变量的地址，所以就是空
char pwd[basesize];
char pwdenv[basesize];

//存储之前的退出码
int lastcode = 0;

string GetName()
{
    string username = getenv("USER");
    return username.empty() ? "None" : username;
}

string GetHostName()
{
    string hostname = getenv("HOSTNAME");
    return hostname.empty() ? "None": hostname;
}

bool repalcePwd()
{
    for(int i = 0; g_env[i]; i++)
    {
        if(strncmp(g_env[i],pwdenv,3) == 0)
        {
            g_env[i] = pwdenv;
            return true;
        }
    }
    return false;
}

string GetPwd()
{
    if(getcwd(pwd,sizeof(pwd)) == nullptr)
        return "Node";
    //将当前的工作路径，保存至环境变量中
    snprintf(pwdenv,sizeof(pwdenv),"PWD=%s",pwd);

    //putenv(pwdenv); //将新的pwd,添加到系统的环境变量表中
    

    repalcePwd();//将新的pwd,添加到自己的环境变量表中
    return pwd;
   // string pwd = getenv("PWD");
   // return pwd.empty() ? "None": pwd;
}

string LastDir()
{
    string pwd = GetPwd();
    if(pwd == "/" || pwd == "None")
        return pwd;
    //寻找最后一个文件夹
    int pos = pwd.rfind("/");

    return pwd.substr(pos+1);

}

//1.显示命令行提示符
void ShowCommandLine()
{
    char command_line[basesize];
    snprintf(command_line,basesize,"[%s@%s %s]#",\
            GetName().c_str(),GetHostName().c_str(),LastDir().c_str());
   printf("%s",command_line); 
   fflush(stdout);
}


//2.读取命令行参数
bool GetCommandLine(char command_buffer[],int size)
{
    //读取一行的用户输入
    char* ret = fgets(command_buffer,size,stdin);
    if(ret == NULL)
    {
        return false; //获取输入失败
    }
    //处理回车键
    command_buffer[strlen(command_buffer) - 1] = '\0';
    if(strlen(command_buffer) == 0)
        return false;

    return true; 
}

void debug()
{
    printf("argc:%d\n",g_argc);
    for(int i=0; g_argv[i]; i++)
    {
        printf("[%d] = %s\n",i,g_argv[i]);
    }
}

void AnalyzeCommandLine(char command_line[])
{
    memset(g_argv,0,sizeof(g_argv));
    g_argc = 0;
    const char sep[10] = " ";//指定分隔符

    g_argv[g_argc++] = strtok(command_line,sep);//先提取第一个

    //strtok读取失败返回null
    while((g_argv[g_argc++] = strtok(nullptr,sep)));//依次提取后面的
    g_argc--;//个数要-1
}


bool ExecCommandLine()
{
    //shell创建子进程执行任务
    pid_t id = fork();
    if(id < 0)
        return false;
    if(id == 0)
    {
        //child执行命令
        execvpe(g_argv[0],g_argv,g_env);
        exit(1);//执行失败，退出码为1
    }
    int status = 0;
    pid_t rid = waitpid(id,&status,0); //阻塞式等待

    if(rid > 0)
    {
        //等待成功
        //1.子进程正常结束
        if(WIFEXITED(status))
        {
            lastcode = WEXITSTATUS(status);
        }
        else 
        {
            lastcode = 99;
        }
        return true;
    }
    return false;
}

void AddEnv(const char* str)
{
    int index = 0;
    while(g_env[index])
    {
        index++;
    }
    g_env[index] = (char*)malloc(strlen(str) + 1);
    strncpy(g_env[index++],str,strlen(str)+1);
    g_env[index] = nullptr;
}

bool CheckAndExecBuildCommand() //判断是否式内建命令
{
    //枚举几个内建命令
    if(strcmp(g_argv[0],"cd") == 0)
    {
        if(g_argc == 2)
        {
            chdir(g_argv[1]);
            lastcode = 0;
        }
        else
        {
            lastcode = 1;
        }
        return  true;
    }
    else if(strcmp(g_argv[0],"export") == 0)
    {
        if(g_argc == 2)
        {
            AddEnv(g_argv[1]);
            lastcode = 0;
        }
        else
        {
            lastcode = 2;
        }
        return true;
    }
    else if(strcmp(g_argv[0], "env") == 0)
    {
        for(int i = 0; g_env[i]; i++)
        {
            printf("%s\n",g_env[i]);
        }
        lastcode = 0;
        return true;
    }
    else if(strcmp(g_argv[0],"echo") == 0)
    {
        if(g_argc == 2)
        {
            if(g_argv[1][0] == '$')
            {
                if(g_argv[1][1] ==  '?')
                {
                    printf("lastcdoe:%d\n",lastcode);
                    lastcode = 0;
                }
            }
            else
            {
                printf("%s\n",g_argv[1]);
                lastcode = 0;
            }
        }
        else
        {
            lastcode = 3;
        }
        return true;
    }
    return false;
}

void InitEnv()
{
    extern char** environ;
    //从系统shell中获取环境变量
    int index = 0;
    while(environ[index])
    {
        int len = strlen(environ[index]);
        g_env[index] = (char*)malloc(len + 1);
        strncpy(g_env[index],environ[index],len + 1);
        index++;
    }
    g_env[index] = nullptr;
}

int main()
{
    InitEnv();
    char command_buffer[basesize];
    while(true)
    {    
        //1.显示命令行提示符
        ShowCommandLine();
        //slsleep(2);
        //printf("\n");
        //2.读取命令行参数
        if( !GetCommandLine(command_buffer,basesize) )
        {
            continue; 
        }
        
        //3.解析命令行参数
        AnalyzeCommandLine(command_buffer);
        //debug();
        if(CheckAndExecBuildCommand()) //判断是否式内建命令
        {
            continue;
        }
        //4.执行命令
        ExecCommandLine();
    }
    return 0;
}
